Vue 2:生命周期、$nextTick、computed 与 watch
按「何时执行钩子」「何时 DOM 可用」「computed 与 watch 如何取舍」三条线展开。
一、生命周期阶段
各阶段含义
| 钩子 | 说明 |
|---|---|
| beforeCreate | 实例创建完成前,data / el 等尚未初始化 |
| created | 实例创建完成,data、methods 等已就绪,模板尚未挂载 |
| beforeMount | 挂载前:已编译模板、生成 VNode,真实 DOM 尚未挂载,$el 常为不可用状态 |
| mounted | 挂载完成:VNode 已 patch 到真实 DOM,$el 可用 |
| beforeUpdate | 数据已变,DOM 尚未更新 |
| updated | DOM 已随数据更新完毕(勿在 updated 里无脑改数据,易死循环) |
| beforeDestroy | 销毁前,适合做清理(定时器、监听、全局事件) |
| destroyed | 销毁完成,子组件也已销毁 |
keep-alive 专属
- activated:缓存组件被激活(回到前台)。
- deactivated:缓存组件被停用(退到后台)。
errorCaptured
可捕获子孙组件错误,用于兜底 UI 或日志(与全局 errorHandler 配合)。
二、执行顺序(洋葱模型)
- 首次挂载:父
beforeCreate→ 父created→ 父beforeMount→ 子beforeCreate→ … → 子mounted→ 父mounted。 - 更新:父
beforeUpdate→ 子beforeUpdate→ 子updated→ 父updated。 - 销毁:父
beforeDestroy→ 子beforeDestroy→ 子destroyed→ 父destroyed。
三、常见实践问题
何时操作 DOM?
在 mounted、updated 中获取 DOM 更稳妥;若需保证整棵子树已更新,结合 this.$nextTick。
何时发 Ajax?
created 与 mounted 均可;created 更早(不等 DOM)。若依赖 DOM 尺寸再请求,选 mounted。
Vue 2 与 Vue 3 钩子对照(简述)
- Vue 3 用
setup(执行时机在beforeCreate~created之间)承接创建阶段逻辑。 - 其余钩子多为
onXxx函数形式。
四、v-if="false" 与渲染
v-if="false" 的模板在初始化时不会渲染,编译阶段即跳过,不会产生对应 VNode/DOM;与 nextTick 无「强行渲染」关系。
五、$nextTick
Vue 如何更新 DOM?
数据变化后,Vue 异步批量刷新视图,合并同一轮事件循环里的多次修改,减少重复渲染。
$nextTick 作用
在 DOM 更新完成后执行回调,用于读取布局(offsetHeight、scrollTop 等)或依赖最新 DOM 的逻辑。
典型场景
修改数据后立刻测量 DOM、聚焦输入框、初始化依赖 DOM 的第三方库等。
六、computed 与 watch
一句话区分
- computed:依赖多个响应式数据,返回一个派生值,有缓存;适合「由 A、B 算出 C」;不适合把重副作用、异步逻辑塞进去。
- watch:监听一个或多个数据源,变化时执行自定义逻辑,无缓存;适合异步请求、日志、联动其它状态。
选用建议
- 推导展示数据 →
computed。 - 数据变化触发副作用(尤其异步) →
watch。 - 避免用
watch去做computed能表达的纯计算,以免冗余。
七、注意事项汇总
- 不要在
updated中无条件修改响应式数据,容易无限循环。 mounted不保证所有子组件都已挂载完毕;需「全家就绪」时用$nextTick或业务上的就绪标志。beforeCreate中勿依赖未初始化的实例字段(早期访问this上尚未就绪的内容需谨慎)。- 销毁清理优先放在
beforeDestroy,destroyed里实例尚可访问但绑定已失效,不宜作为主清理点。 - keep-alive 缓存的组件不会走
destroyed,而走deactivated。
